#include "UWindowThemer.h"
#include "UTextureManager.h"
#include "UDockable.h"
#include "Controls/UThemedButton.h"
#include "UControlContainer.h"
#include "UStringUtils.h"
#include <fstream>
#include <iostream>
#include <sstream>

#include <boost/algorithm/string.hpp>
using namespace std;
using std::max;

// #define THEME_NAME "Classic98"
#define THEME_NAME "Vistash"
#define ROUND(x) ((int)((x)+0.5))

TWindowThemer *wt;

TWindowThemer::TWindowThemer() {
	load(THEME_NAME);
}

#define GET_THEMEELEMENT(bname) te->push_back(GetThemeElement(#bname, inifile, tm, path, "border"))
#define GET_BUTTON(bname) te->push_back(GetButtonThemeElement(#bname, inifile, tm, path))

TThemeElement* TWindowThemer::GetThemeElement(string bname, TIniFile& inifile, TTextureManager& tm,
                              string path, string section) {
	string v;
	vector<string> results;

	v = inifile.getValue(section, bname);

	if(v != "") {
		boost::split(results, v, boost::is_any_of(" "));
		string texturename="";
		bool flipx=false;
		bool flipy=false;
		bool clampx = false, clampy = false, blend = false;
		for(vector<string >::iterator i = results.begin(); i!= results.end(); ++i) {
			if(*i == "mirror-x") flipx=true; else
			if(*i == "mirror-y") flipy=true; else
			if(*i == "clamp-x") clampx=true; else
			if(*i == "clamp-y") clampy=true; else
			if(*i == "blend") blend=true; else
			texturename = *i;
		}
		GLuint tx = tm.getTexture(path+texturename, true, blend ? GL_LINEAR : GL_NEAREST, blend ? GL_LINEAR : GL_NEAREST, true);
		//GLuint tx = tm.getTexture(path+texturename, true);
		if (clampx) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
		if (clampy) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T , GL_CLAMP);
		int tw,th;
		glGetTexLevelParameteriv(GL_TEXTURE_2D,0, GL_TEXTURE_WIDTH, &tw);
		glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &th);
// 		cerr << STRFORMAT(stderr, "%s: flipx=%s, flipy=%s\n",bname.c_str(), flipx?"true":"false", flipy?"true":"false");
		return new TThemeElement(tx, tw, th, flipx, flipy );
	}
	return NULL;
}

TThemedButton* TWindowThemer::GetButtonThemeElement(string bname, TIniFile& inifile, TTextureManager &tm, string path ) {
	string v;
	vector<string> results;
	GLfloat w=0;
	GLfloat h=0;
	GLfloat prx=0;
	GLfloat pry=0;
	GLfloat pax=0;
	GLfloat pay=0;
	results.clear();
	v = inifile.getValue("buttons", bname + "_position_x");
	if(v != "") {
		boost::split(results, v, boost::is_any_of(" "));
		for(vector<string >::iterator i = results.begin(); i!= results.end(); ++i) {
			if(i->find("%") != string::npos) {
				std::stringstream iss;
				iss << *i;
				iss >> prx;
				prx *= 0.01;//procenten
			}
			else {
				std::stringstream iss;
				iss << *i;
				iss >> pax;
			}
		}
	}
	results.clear();
	v = inifile.getValue("buttons", bname + "_position_y");
	if(v != "") {
		boost::split(results, v, boost::is_any_of(" "));
		for(vector<string >::iterator i = results.begin(); i!= results.end(); ++i) {
			if(i->find("%") != string::npos) {
				std::stringstream iss;
				iss << *i;
				iss >> pry;
				pry *= 0.01;//procenten
			}
			else {
				std::stringstream iss;
				iss << *i;
				iss >> pay;
			}
		}
	}
	results.clear();
	v = inifile.getValue("buttons", bname + "_size");
	if(v != "") {
		boost::split(results, v, boost::is_any_of("x"));
		if(results.size()>1) {
			{std::stringstream iss;	iss << results[0];iss >> w;}
			{std::stringstream iss;	iss << results[1];iss >> h;}
		}
	}
	vector<string> strs;
	static char* focusstr[2] = {"focused" , "unfocused"};
	static char* statestr[3] = {"normal" , "highlight", "clicked"};
	string elements[2][3];
	for (int i = 0; i < 2; ++i) {
		for (int j = 0; j < 3; ++j) {
			elements[i][j] = inifile.getValue("buttons", bname+"_"+focusstr[i]+"_"+statestr[j]);
			if (i == 1 && elements[i][j] == "") elements[i][j] = elements[0][j];
			if (j != 0 && elements[i][j] == "") elements[i][j] = elements[i][j-1];
			strs.push_back(path+elements[i][j]);
		}
	}

	pay -= borderTheme->BorderTop();
	float pos[4] = {prx,pax,pry,pay};
	return new TThemedButton(&strs[0], pos, w, h);
}

bool TWindowThemer::load(std::string themename) {
	cerr << STRFORMAT("Loading window theme(r)...\n");
	string path="Data/WindowThemes/"+themename+"/";
	string themefile=path+"theme.ini";
	tfname = themefile;
	tfpath = path;
	TIniFile inifile(themefile);
	vector<TThemeElement* > *te = new vector<TThemeElement* >;
	vector<string > results;
	TTextureManager tm;
	string v;
	//Get border widths
	GLfloat border_left_width=0;
	GLfloat border_right_width=0;
	GLfloat border_top_width=0;
	GLfloat border_bottom_width=0;
	v = inifile.getValue("border","border_left_width");
	if(v != "") {
		std::stringstream iss;
		if((v.find("%"))!=std::string::npos) {
			 iss << v;
			 iss >> border_left_width;
			 border_left_width=-border_left_width;
		}
		else {
			iss << v;
			iss >> border_left_width;
		}
	}
	v = inifile.getValue("border","border_right_width");
	if(v != "") {
		std::stringstream iss;
		if(v.find("%")!=std::string::npos) {
			 iss << v;
			 iss >> border_right_width;
			 border_right_width=-border_right_width;
		}
		else {
			iss << v;
			iss >> border_right_width;
		}
	}
	v = inifile.getValue("border","border_top_width");
	if(v != "") {
		std::stringstream iss;
		if(v.find("%")!=std::string::npos) {
			 iss << v;
			 iss >> border_top_width;
			 border_top_width=-border_top_width;
		}
		else {
			iss << v;
			iss >> border_top_width;
		}
	}
	v = inifile.getValue("border","border_bottom_width");
	if(v != "") {
		std::stringstream iss;
		if(v.find("%")!=std::string::npos) {
			 iss << v;
			 iss >> border_bottom_width;
			 border_bottom_width=-border_bottom_width;
		}
		else {
			iss << v;
			iss >> border_bottom_width;
		}
	}

	//Get textures for corners and sides
	GET_THEMEELEMENT(corner_top_left);
	GET_THEMEELEMENT(border_top);
	GET_THEMEELEMENT(corner_top_right);
	GET_THEMEELEMENT(border_left);
	GET_THEMEELEMENT(border_right);
	GET_THEMEELEMENT(corner_bottom_left);
	GET_THEMEELEMENT(border_bottom);
	GET_THEMEELEMENT(corner_bottom_right);

	//Get buttons
//	GET_BUTTON(button_close);
//	GET_BUTTON(button_minimize);
//	GET_BUTTON(button_maximize);

	borderTheme = new TBorderTheme(te, border_left_width, border_right_width, border_top_width, border_bottom_width);
	//ector<string> titlecolor;
	results.clear();
	v = inifile.getValue("title", "color");
	boost::split(results, v, boost::is_any_of(" "));
	if (results.size() == 3) {
		for (int i = 0 ; i < 3; ++i) {
			borderTheme->titlecolor[i] = atoi(results[i].c_str());
		}
	}

	{
		string	fontname = inifile.getValue("title", "font");
		int size = 9;
		v = inifile.getValue("title", "size");
		if (v != "") size = atoi(v.c_str());
		GLFont = TGLFontRef(fontname, size);
	}

	return false;
}

void TWindowThemer::draw(TDockable *window, bool hasFocus, int mouseX, int mouseY) {
	glPushMatrix();
		//Draw borders
		glTranslatef(window->Left, window->Top , 0);
		borderTheme->draw(window, hasFocus, mouseX, mouseY); //FIXME: Y inverted?

		//Titlebar


		glColor3ubv(&BorderTheme()->titlecolor[0]);
		if (window->WindowCaptionRenderer==NULL)
			window->WindowCaptionRenderer= new TGLFontObj(
															GLFont,
															TRectangle(0, -BorderTheme()->BorderTop(), window->Width(), 0),
															TP_Left,
															TP_VCenter,
															window->Name.c_str()
														   );

		window->WindowCaptionRenderer->Draw();
		/*GLFont->drawText(TRectangle(0, -BorderTheme()->BorderTop(), window->Width(), 0),
															TP_Left,
															TP_VCenter,
															window->Name.c_str()
															);*/
	glPopMatrix();
}

TWindowThemer::~TWindowThemer() {
}

WT_MOUSEHIT_RESULT TBorderTheme::mouseHit(const TDockable *window, const int &x,const int &y) {
	if( (x < window->Left - borderWidthLeft) || (x > window->Right + borderWidthRight) ||
	   (y < window->Top - borderWidthTop) || (y > window->Bottom + borderWidthBottom))
	   return WT_MOUSEHIT_OUTSIDE_BORDER;
	//We're at least inside the border
	if( x <= window->Left) {//left border
		if(y <= window->Top) return WT_MOUSEHIT_RESIZE_TOP_LEFT;
		if(y >= window->Bottom) return WT_MOUSEHIT_RESIZE_BOTTOM_LEFT;
		if(y > window->Top) return WT_MOUSEHIT_RESIZE_LEFT;
	}
	if(x>window->Left) {
		if(y <= window->Top)
			if(x>=window->Right) return WT_MOUSEHIT_RESIZE_TOP_RIGHT;
				else { //test hit button
					int ww = window->Right-window->Left;
					int wh = window->Bottom-window->Top;
					int mx = (int)(x - window->Left - borderWidthLeft);
					int my = (int)(y - window->Top + borderWidthTop);
/*
					if(((TButtonThemeElement*)(*telem)[TWindowThemer::button_close   ])->
						hitTest( ww, wh, mx, my )) return WT_MOUSEHIT_BUTTON_CLOSE;
					if(((TButtonThemeElement*)(*telem)[TWindowThemer::button_minimize])->
						hitTest( ww, wh, mx, my )) return WT_MOUSEHIT_BUTTON_MINIMIZE;
					if(((TButtonThemeElement*)(*telem)[TWindowThemer::button_maximize])->
						hitTest( ww, wh, mx, my )) return WT_MOUSEHIT_BUTTON_MAXIMIZE;
					if( y <= window->Top-borderWidthTop+borderWidthBottom)
						return WT_MOUSEHIT_RESIZE_TOP;
					else
*/
						return WT_MOUSEHIT_MOVEABLE;
				}
		if(y >= window->Bottom)
			if(x<window->Right) return WT_MOUSEHIT_RESIZE_BOTTOM;
				else return WT_MOUSEHIT_RESIZE_BOTTOM_RIGHT;
		if(x > window->Right) return WT_MOUSEHIT_RESIZE_RIGHT;
	}
	return WT_MOUSEHIT_INSIDE_BORDER;
}

WT_MOUSEHIT_RESULT TWindowThemer::mouseHit(const TDockable *window, const int &x, const int &y) {
	return borderTheme->mouseHit(window, x,y);
}

TBorderTheme::TBorderTheme(vector<TThemeElement* > *telems, GLfloat borderWidthLeft,
                                                           GLfloat borderWidthRight,
                                                           GLfloat borderWidthTop,
                                                           GLfloat borderWidthBottom) {
	telem = telems;
	this->borderWidthLeft = borderWidthLeft;
	this->borderWidthRight = borderWidthRight;
	this->borderWidthTop = borderWidthTop;
	this->borderWidthBottom = borderWidthBottom;
}

void TBorderTheme::draw(const TDockable *window, bool hasFocus, int mouseX, int mouseY) {
		int width = window->Right - window->Left;
		int height = window->Bottom - window->Top;

	glPushMatrix();
		//border top
 		if (hasFocus)
			glColor3f(1,1,1);
 		else
 			glColor3f(0.5, 0.5, 0.5);
		glTranslatef(-borderWidthLeft, -borderWidthTop, 0);
		(*telem)[TWindowThemer::top_left]->draw(borderWidthLeft, borderWidthTop);
		glTranslatef(borderWidthLeft,0, 0);
		(*telem)[TWindowThemer::top]->draw(width, borderWidthTop);
		glTranslatef(width,0, 0);
		(*telem)[TWindowThemer::top_right]->draw(borderWidthRight, borderWidthTop);

		//border sides
		glTranslatef(-(borderWidthRight+width), borderWidthTop, 0);
		(*telem)[TWindowThemer::left]->draw(borderWidthLeft,height);
		glTranslatef(borderWidthLeft+width, 0, 0);
		(*telem)[TWindowThemer::right]->draw(borderWidthRight,	height);

		//border bottom
		glTranslatef(-(borderWidthRight+width), height, 0);
		(*telem)[TWindowThemer::bottom_left]->draw(borderWidthLeft, borderWidthBottom);
		glTranslatef(borderWidthLeft,0, 0);
		(*telem)[TWindowThemer::bottom]->draw(width, borderWidthBottom);
		glTranslatef(width,0, 0);
		(*telem)[TWindowThemer::bottom_right]->draw(borderWidthRight, borderWidthBottom);

		//buttons
		/*
		uint32 btnState;
		glTranslatef(-width, -height-borderWidthTop,0);
		static WT_MOUSEHIT_RESULT btnhits[3] = {WT_MOUSEHIT_BUTTON_CLOSE, WT_MOUSEHIT_BUTTON_MINIMIZE, WT_MOUSEHIT_BUTTON_MAXIMIZE};

		for (int i = 0; i < 3; ++i) {
			btnState = TButtonThemeElement::active_normal;
			if (mouseHit(window, mouseX, mouseY)==btnhits[i]) {
				//if (
				btnState = TButtonThemeElement::active_highlight;
			}
			if (!hasFocus) btnState += 3;
			((TButtonThemeElement*)(*telem)[TWindowThemer::button_close+i])->draw(btnState,width, height);
		}
		*/
		glPopMatrix();
}

void TThemeElement::draw(GLfloat width, GLfloat height) {
	glEnable(GL_BLEND);
	if(texture!=GL_ZERO) {
		glEnable(GL_TEXTURE_2D);
		glEnable(GL_BLEND);
		glBindTexture(GL_TEXTURE_2D, texture);
	}
	else {
		glDisable(GL_TEXTURE_2D);
		glDisable(GL_BLEND);
		glColor3f(1.0,0.0,1.0);
	}
	int textureLeft = flipx?1:0;
	int textureTop = flipy?1:0;
	int textureRight = 1 - textureLeft;
	int textureBottom = 1 - textureTop;
	int flipXCorrection = flipx?texturewidth-ROUND(width):0;
	int flipYCorrection = flipy?textureheight-ROUND(height):0;
	int w = max(texturewidth, ROUND(width));
	int h = max(textureheight, ROUND(height));
	glBegin(GL_QUADS);
		glTexCoord2f(textureLeft , textureTop);     glVertex2f(0 - flipXCorrection, 0 - flipYCorrection);
		glTexCoord2f(textureRight, textureTop);     glVertex2f(w - flipXCorrection, 0 - flipYCorrection);
		glTexCoord2f(textureRight, textureBottom);  glVertex2f(w - flipXCorrection, h - flipYCorrection);
		glTexCoord2f(textureLeft , textureBottom);  glVertex2f(0 - flipXCorrection, h - flipYCorrection);
	glEnd();
	glDisable(GL_BLEND);
}

TThemeElement::TThemeElement() {
	flipx=GL_ZERO;
	flipy=32;
	texture=32;
	texturewidth=false;
	textureheight=false;
}


TThemeElement::TThemeElement(GLuint tex, int tw, int th, bool fx, bool fy) {
	flipx=fx;
	flipy=fy;
	texture=tex;
	texturewidth=tw;
	textureheight=th;
}

TThemeElement::TThemeElement(TThemeElement &te) {
	flipx=te.getFlipx();
	flipy=te.getFlipy();
	texture=te.getTexture();
	texturewidth=te.getTextureWidth();
	textureheight=te.getTextureHeight();
}

bool TThemeElement::getFlipx() {
	return flipx;
}

bool TThemeElement::getFlipy() {
	return flipy;
}

GLuint TThemeElement::getTexture() {
	return texture;
}

int TThemeElement::getTextureWidth() {
	return texturewidth;
}

int TThemeElement::getTextureHeight() {
	return textureheight;
}

TThemeElement::~TThemeElement() {
}

TButtonThemeElement::TButtonThemeElement(vector<TThemeElement *> *btns, GLfloat w, GLfloat h, GLfloat prx, GLfloat pry, GLfloat pax, GLfloat pay) {
	buttons = btns;
	width  = w;
	height = h;
	posrelativex = prx;
	posabsolutex = pax;
	posrelativey = pry;
	posabsolutey = pay;
}

bool TButtonThemeElement::hitTest(int windowWidth, int windowHeight, int mousex, int mousey) {
	int btnx = (int)((windowWidth-width)*posrelativex + posabsolutex);
	int btny = (int)((windowHeight-height)*posrelativey + posabsolutey);
	bool a = (btnx<=mousex);
	bool b = (mousex<=btnx+width);
	bool c = (btny<=mousey);
	bool d = (mousey<=btny+height);
	bool e = a&&b;
	bool f = c&&d;
	bool g = e&&f; //((btnx>=mousex)&&(btnx+width<=mousex))&&((mousey>=btny)&&(btny+height>=mousey));
	return g;
}

void TButtonThemeElement::draw(uint32 state, GLfloat windowWidth, GLfloat windowHeight) {
	glPushMatrix();
// 	GLfloat btnOffset = (state-active_normal) * height;
// 	glTranslatef((windowWidth-width)*posrelativex + posabsolutex, (windowHeight-height)*posrelativey + posabsolutey - btnOffset, 0);
// 	glScissor(0, 0, ROUND(width), ROUND(height));
// 	glEnable(GL_SCISSOR_TEST);
	glTranslatef((windowWidth-width)*posrelativex + posabsolutex, (windowHeight-height)*posrelativey + posabsolutey, 0);
	(*buttons)[state]->draw(width, height);
// 	(*buttons)[buttonState]->draw(width, height);
// 	glDisable(GL_SCISSOR_TEST);
	glPopMatrix();
}

TButtonThemeElement::~TButtonThemeElement() {
};

void TWindowThemer::UpdateControls(TControlContainer* controls, TRectangle rect)
{
	TThemedButton* buttonclose = (TThemedButton*)controls->GetControl(0);
	TThemedButton* buttonmaximize = (TThemedButton*)controls->GetControl(1);
	TThemedButton* buttonminimize = (TThemedButton*)controls->GetControl(2);
	buttonclose->setPosRelative(rect);
	buttonmaximize->setPosRelative(rect);
	buttonminimize->setPosRelative(rect);
}

void TWindowThemer::SetupControls(TDockable* dock)
{
 	TControlContainer* controls = dock->themecontrols;
	TIniFile inifile(tfname);
	TThemedButton* button;
	button = GetButtonThemeElement("button_close", inifile, *GTextureManager, tfpath);
	button->onClick = boost::bind(&TDockable::Close, dock);
	controls->AddControl(button);
	button = GetButtonThemeElement("button_maximize", inifile, *GTextureManager, tfpath);
	controls->AddControl(button);
	button = GetButtonThemeElement("button_minimize", inifile, *GTextureManager, tfpath);
	controls->AddControl(button);
}
